#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"

#define LED_SERVICE "HDF_GENERIC_LED"

#define LED_WRITE_READ_BY_GPIO_NUM 1  // 读写操作码，通过GPIO编号指定
#define LED_WRITE_READ_BY_LABEL 2     // 读写操作码，通过led的label指定
#define LED_WRITE_READ_BY_ORDER 3     // 读写操作码，通过注册顺序指定

typedef enum LED_state {
	LED_OFF = 0,
	LED_ON,
	LED_TOGGLE,
} LED_state;

static int check_real_zero(char* str);

/*
 * @brief: 将信息打包成uint64结构
 * 下面是格式：
 * 方式1 使用gpionum指定，64bit的数据，第1个字节为 LedStatus, 后4个字节是用于指定的gpionum
 * 方式2 使用label指定，传递字符串，字符串第一个字符是'0' '1' '2' 对应 LedStatus，之后就是label
 * 方式3 使用注册顺序指定，64bit的数据，第1个字节为 LedStatus, 后4个字节是用于指定的注册顺序（从0开始）
 */
static int package_sbuf_data(uint8_t cmd_code, char* led_desc, int num, uint8_t led_status, struct HdfSBuf *sent_data);

static int SendEvent(struct HdfIoService *serv, char* handle_type, char* led_desc, uint8_t led_status)
{
	int ret = 0;
	uint8_t cmd_code;
	int num;
	struct HdfSBuf *sent_data = HdfSBufObtainDefaultSize(); // HdfSbufWriteUint8
	struct HdfSBuf *reply_data;
	int led_cur_status;
	// writeUnpadBuffer is not supported on 0 sbuf if use HdfSBufObtainDefaultSize
	// 小型系统不支持 HdfSbufTypedObtain(SBUF_IPC) HdfSbufWriteUnpadBuffer
	// struct HdfSBuf *sent_data = HdfSbufTypedObtain(SBUF_IPC); // HdfSbufWriteUnpadBuffer
	if (!sent_data)
	{
		printf("fail to obtain sbuf data!\r\n");
		return 1;
	}

	cmd_code = LED_WRITE_READ_BY_GPIO_NUM;
	num = atoi(led_desc);
	if (!num)
	{
		ret = check_real_zero(led_desc); // 字符串是不是 "0" "   0" 从而进一步判断是不是数字或者label
		if (ret)
		{
			cmd_code = LED_WRITE_READ_BY_LABEL;
		}
	}
	// 还是等于1，代表不是方式2，检查 handle_type 是不是3 来区分方式1还是3
	if (cmd_code == LED_WRITE_READ_BY_GPIO_NUM) {
		if (!strcmp(handle_type, "3"))
		{
			cmd_code = LED_WRITE_READ_BY_ORDER;
		}
	}
	if (cmd_code < LED_WRITE_READ_BY_GPIO_NUM || cmd_code > LED_WRITE_READ_BY_ORDER)
	{
		return EXIT_FAILURE;
	}

	// 制作 sent_data 供Dispatch调用
	ret = package_sbuf_data(cmd_code, led_desc, num, led_status, sent_data);
	if (ret)
	{
		return EXIT_FAILURE;
	}

	// Dispatch 返回的 Sbuf
	reply_data = HdfSBufObtainDefaultSize();
	if (reply_data == NULL)
	{
		printf("fail to obtain sbuf reply_data!\r\n");
		ret = HDF_DEV_ERR_NO_MEMORY;
		goto out;
	}

	/* 通过Dispatch发送到驱动 */
	ret = serv->dispatcher->Dispatch(&serv->object, cmd_code, sent_data, reply_data);
	if (ret != HDF_SUCCESS)
	{
		printf("fail to send service call!\r\n");
		goto out;
	}

	led_cur_status = 0;
	/* 读取驱动的回复数据 */
	if (!HdfSbufReadInt32(reply_data, &led_cur_status))
	{
		printf("fail to get service call reply!\r\n");
		ret = HDF_ERR_INVALID_OBJECT;
		goto out;
	}
	printf("Get reply is: %d (LED state)\r\n", led_cur_status); // 返回的数据是当前led的状态，点亮(1)或者熄灭(0)
out:
	HdfSBufRecycle(sent_data);
	HdfSBufRecycle(reply_data);
	return ret;
}

int main(int argc, char **argv)
{
	int i;

	if (argc != 4)
	{
		printf("%s [type] [led_desc] [led_status]\n", argv[0]);
		printf("type       : 1 mean gpio num 2 mean led label 3 mean led num\n");
		printf("led_desc   : maybe gpionum maybe led label(hcs file)\n");
		printf("led_status : 0 OFF 1 ON 2 toggle\n");
		return -1;
	}

	/* 获取服务 */
	struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE);
	if (serv == NULL)
	{
		printf("fail to get service %s!\n", LED_SERVICE);
		return HDF_FAILURE;
	}

	for (i = 0; i < argc; i++)
	{
		printf("Argument %d is %s.\n", i, argv[i]);
	}

	SendEvent(serv, argv[1], argv[2], atoi(argv[3]));

	HdfIoServiceRecycle(serv);
	printf("exit\n");

	return HDF_SUCCESS;
}

/*
 * @brief: 检查字符串是否为数字0
 * @param[in]: 参数1(str): 字符串
 * @return: 如果是数字0，那么返回 EXIT_SUCCESS 否则返回 EXIT_FAILURE
 */
static int check_real_zero(char* str)
{
	int i;
	int len;
	len = strlen(str);
	--len;
	if (str[len] != '0') // "0" "        0"
	{
		return EXIT_FAILURE;
	}
	for (i = 0; i < len; ++i)
	{
		if (str[i] != ' ') // "0" "        0"
			return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}

#define PACKAGE_SBUF_DATA_TO_UINT64(data, num, led_status, sent_data) data[0] = led_status; \
																			memcpy(data + 4, &num, 4); \
																			if (!HdfSbufWriteUint64(sent_data, *((uint64_t*)data))) { \
																				printf("fail to write sbuf!\r\n"); \
																				return EXIT_FAILURE; \
																			}

/*
 * @brief: 将信息打包成uint64结构
 * 下面是格式：
 * 方式1 使用gpionum指定，64bit的数据，第1个字节为 LedStatus, 后4个字节是用于指定的gpionum
 * 方式2 使用label指定，传递字符串，字符串第一个字符是'0' '1' '2' 对应 LedStatus，之后就是label
 * 方式3 使用注册顺序指定，64bit的数据，第1个字节为 LedStatus, 后4个字节是用于指定的注册顺序（从0开始）
 */
static int package_sbuf_data(uint8_t cmd_code, char* led_desc, int num, uint8_t led_status, struct HdfSBuf *sent_data)
{
	char* data;
	printf("cmd code is %d\n", cmd_code);
	data = NULL;
	if (cmd_code == 1)
	{
		data = (char*)calloc(sizeof(char), 8);
		PACKAGE_SBUF_DATA_TO_UINT64(data, num, led_status, sent_data);
	}
	else if (cmd_code == 2)
	{
		int desc_len;
		desc_len = strlen(led_desc);

		data = (char*)calloc(sizeof(char), 1 + desc_len + 1); // led_status + led_desc + '\0'

		data[0] = '0' + led_status;
		memcpy(data + 1, led_desc, desc_len);

		// 64bit的数据写入 Sbuf
		if (!HdfSbufWriteString(sent_data, data))
		{
			printf("fail to write sbuf!\r\n");
			return EXIT_FAILURE;
		}
	}
	else if (cmd_code == 3)
	{
		data = (char*)calloc(sizeof(char), 8);
		PACKAGE_SBUF_DATA_TO_UINT64(data, num, led_status, sent_data);
	}
	if (!data)
	{
		return EXIT_FAILURE;
	}

	// 展示要写入的数据
	if (cmd_code == 1)
	{
		int i;
		for (i = 0; i < 8; ++i) {
			printf("%.2x ", (unsigned char)data[i]);
		}
		printf("\n");
	}
	else if (cmd_code == 2)
	{
		printf("%s\n", data);
	}
	else if (cmd_code == 3)
	{
		int i;
		for (i = 0; i < 8; ++i) {
			printf("%.2x ", (unsigned char)data[i]);
		}
		printf("\n");
	}

	free(data);
	data = NULL;

	return EXIT_SUCCESS;
}